package org.haredot.web.dispatcher;

import com.alibaba.fastjson2.JSONArray;
import org.haredot.annotation.ResponseBody;
import org.haredot.context.WebApplicationContext;
import org.haredot.media.MediaType;
import org.haredot.utils.WebUtils;
import org.haredot.web.*;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;


public class DispatcherServlet extends HttpServlet {

    private String prefix = "/WEB-INF/pages/" ;

    private String suffix = ".jsp" ;

    /**
     * 维护一个 web 应用上下文 ；
     */
    private WebApplicationContext webApplicationContext ;

    @Override
    public void init(ServletConfig config) throws ServletException {
        String prefixView =  config.getInitParameter("prefix");

        if (prefixView !=null && !"".equals(prefixView)) {
            this.prefix = prefixView ;
        }

        String suffix = config.getInitParameter("suffix");

        if (suffix !=null && !"".equals(suffix)) {
            this.suffix = suffix ;
        }
        // 初始化 WebApplicationContext
        webApplicationContext = new WebApplicationContext();

    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            // 将 请求 和响应 添加到 工具类WebUtils中
            WebUtils.setRequestAndResponse(req, resp);

            // 1. 获取 请求地址 , 例如 /user/add
            String servletPath = req.getServletPath();

            // 2. 获取 请求方式
            String method = req.getMethod();

            // 2. 根据 请求地址 和 请求方式 获取该请求 对应的 控制器对象，控制器方法， 方法参数列表
            List<RequestMapping> requestMappings = RequestMappingHandler.parseUrlPattern(servletPath, webApplicationContext.getRequestMappings());

            // 如果 requestMapping 为空 ， 说明 输入的请求地址 没有找到 对应的控制器
            if (requestMappings == null || requestMappings.isEmpty()) {
                // 请求失败，请求地址找不到， 进入 404 页面
                resp.sendError(HttpServletResponse.SC_NOT_FOUND);
                return;
            }
            //
            RequestMapping mapping = null;
            // 如果找到了， 获取 找到的方法 支持的请求方式
            for (RequestMapping requestMapping : requestMappings) {
                // 比较 请求中的 请求方式 是否 和当前请求方式一致
                if (requestMapping.getRequestMethods().contains(RequestMethod.valueOf(method))) {
                    mapping = requestMapping;
                    break;
                }
            }
            if (mapping == null) {
                // 请求方式不正确， 错误编码是 405
                resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
                return;
            }

            // 提供钩子，允许用户扩展逻辑 拦截器 Interceptor

            // 如果 能够正常找到， 则 通过 反射 调用 控制层中对应的方法，完成处理
            Object ret = mapping.invokeExecute(req);


            if (ret == null) return;  // 如果返回 void ,则框架什么都不做

            if (ret instanceof String viewName && !mapping.getMethod().isAnnotationPresent(ResponseBody.class)) {
                ret = new ModelAndView(viewName);
            }

            // 判断 返回的结果是不是 ModelAndView 类型
            if (ret instanceof ModelAndView mav) {
                // 获取视图名
                String viewName = mav.getViewName();
                // 获取 视图模型中的 数据
                Map<String, Object> data = mav.getData();

                if (viewName.startsWith("forward:")) {
                    String urlPattern = viewName.substring(8);
                    // 遍历数据并把 数据存储到 request 作用域中
                    data.forEach(req::setAttribute);
                    req.getRequestDispatcher(urlPattern).forward(req, resp);
                } else if (viewName.startsWith("redirect:")) {
                    String urlPattern = viewName.substring(9);
                    resp.sendRedirect(req.getContextPath() + urlPattern);
                } else { // 转发到 .jsp 页面中
                    data.forEach(req::setAttribute);
                    req.getRequestDispatcher(prefix + viewName + suffix).forward(req, resp);
                }
            } else if (ret instanceof ResponseEntity entity) { // 说明要进行下载操作
                // 实现下载流程
                resp.setContentType(entity.getContentType());

                String att = entity.isAttachment() ? "attachment" : "inline";
                resp.setHeader("Content-Disposition", att + ";filename=" + URLEncoder.encode(entity.getFilename(), "UTF-8"));

                // 下载
                try (ServletOutputStream outputStream = resp.getOutputStream();) {
                    outputStream.write(entity.getBytes());
                }
            } else if (mapping.getMethod().isAnnotationPresent(ResponseBody.class)) {
                // 判断 执行的方法上是否有一个 @ResponseBody 注解，如果有，则返回 JSON
                String jsonString = JSONArray.toJSONString(ret);
                // 添加响应的头
                resp.setContentType(MediaType.APPLICATION_JSON_STRING);
                resp.getWriter().write(jsonString);
            }
        }finally {
            WebUtils.removeRequestAndResponse();
        }

    }
}
